package br.edu.utfpr.backupTools.controller;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
import java.util.List;

import br.edu.utfpr.backupTools.controller.interfaces.ControladorBackup;
import br.edu.utfpr.backupTools.controller.validator.Validator;
import br.edu.utfpr.backupTools.model.dao.interfaces.DaoBackup;
import br.edu.utfpr.backupTools.model.pojo.Backup;
import br.edu.utfpr.backupTools.util.backup.ZipUtil;
import br.edu.utfpr.backupTools.util.enums.Acao;
import br.edu.utfpr.backupTools.util.enums.TipoConexao;
import br.edu.utfpr.backupTools.view.action.strategy.Control;
import br.edu.utfpr.backupTools.view.telas.principal.JanelaPrincipal;
import br.edu.utfpr.comum.BackupDTO;
import br.edu.utfpr.comum.RetornoValidacao;
import br.edu.utfpr.comum.Stream;
import br.edu.utfpr.comum.md5.Hash;

public class ControladorBackupImpl extends ControladorBackup{

    ZipUtil zip = new ZipUtil();
	
	public ControladorBackupImpl(DaoBackup dao, Validator<Backup> validator){
		this.dao = dao;
		this.validator = validator;
	}

    /**
     * Gera todos os backups locais
     */
    @Override
	public RetornoValidacao gerarTodosBackups(){
        List<Backup> backups = dao.list();
		JanelaPrincipal.addLog("Rotina de backups inciada");
		
		for (Backup backup : backups) {
			if(backup.getTipoConexao().equals(TipoConexao.LOCAL))
			{
				try {
					String fileName =  backup.getOrigem().substring(backup.getOrigem().lastIndexOf("/"), backup.getOrigem().length()) + ZipUtil.PREFIXO + ZipUtil.EXT;
					
					RetornoValidacao rv = zip.compress(new File(backup.getOrigem()), new File(backup.getDestino() + fileName));

                    if(!rv.isOk())
                        return rv;

//                    atualizaData(backup);
					JanelaPrincipal.addLog("Backup '"+backup.getDescricao()+"' gerado com sucesso em : "+backup.getDestino() + fileName);
				} catch (Exception e) {
					e.printStackTrace();
					JanelaPrincipal.addLog("Ocorreu um erro a gerar o backup: "+backup.getDescricao());
				}
			}
		}
        return new RetornoValidacao();
	}

    /**
     * Gera o backup local de um determinado backup
     *
     * @param backup
     * @param log lançar ou não logs
     */
    @Override
	public RetornoValidacao gerarBackup(Backup backup, boolean log) {
			try {
                backup = dao.load(backup.getId());
                String fileName =  backup.getOrigem().substring(backup.getOrigem().lastIndexOf("/"), backup.getOrigem().length()) + ZipUtil.PREFIXO + ZipUtil.EXT;
				
				RetornoValidacao rv = zip.compress(new File(backup.getOrigem()), new File(backup.getDestino() + fileName));
                if(!rv.isOk())
                    return  rv;

				if(log)
					JanelaPrincipal.addLog("Backup '"+backup.getDescricao()+"' gerado com sucesso em : "+backup.getDestino() + fileName);

//                atualizaData(backup);
                return rv;
			} catch (Exception e) {
				e.printStackTrace();
				JanelaPrincipal.addLog("Ocorreu um erro ao gerar o backup: "+backup.getDescricao());
				return new RetornoValidacao("Ocorreu um erro a gerar o backup: "+backup.getDescricao());
			}
	}

    /**
     * Gera restore local de um backup
     *
     * @param backup
     */
    @Override
	public RetornoValidacao gerarRestore(Backup backup) {
		try {
			String fileName =  backup.getOrigem().substring(backup.getOrigem().lastIndexOf("/"), backup.getOrigem().length()) + ZipUtil.PREFIXO + ZipUtil.EXT;
            File arquivo = new File(backup.getDestino() + fileName);

            if(!arquivo.exists()){
                return  new RetornoValidacao("Não existe backup gerado para: '"+backup.getDescricao()+"'");
            }

            zip.extrairZip(arquivo, new File((String) backup.getOrigem().subSequence(0, backup.getOrigem().lastIndexOf("/"))));
			return new RetornoValidacao();
		} catch (Exception e) {
			e.printStackTrace();
			JanelaPrincipal.addLog("Ocorreu um erro ao gerar o restore");
			return new RetornoValidacao("Ocorreu um erro a gerar o restore");
		}
	}

	@Override
	public Stream gerarBackupRemoto(Backup backup) throws IOException {
		gerarBackup(backup,false);
		return zip.geraStream(backup);
	}

    /**
     * Metodo que recebe os bytes vindos do servidor e grava o arquivo no cliente, realizando o processo de restore remoto.
     *
     * @param stream
     * @return
     * @throws IOException
     */
    @Override
    public RetornoValidacao gerarRestoreRemoto(Stream stream, Backup backup) throws IOException {
        try{
            //monta o nome do arquivo e caminho da pasta de origem
            String fileName =  backup.getOrigem().substring(backup.getOrigem().lastIndexOf("/"), backup.getOrigem().length()) + ZipUtil.PREFIXO + ZipUtil.EXT;
            String diretorio = (String) backup.getOrigem().subSequence(0, backup.getOrigem().lastIndexOf("/"));

            //Grava o arquivo zipado
            FileOutputStream fos = new FileOutputStream(diretorio + fileName);
            fos.write(stream.getBytes());
            fos.close();

            //verifica o hash md5 do arquivo
            RetornoValidacao rv = isMd5(backup,stream);
            if(!rv.isOk()){
                return  rv;
            }

            //extrai o arquivo zip
            zip.extrairZip(new File(diretorio + fileName), new File(diretorio));

            //deleta o zip depois de extrair
            new File(diretorio + fileName).delete();

            return rv;
        }catch (Exception e){
            e.printStackTrace();
            return new RetornoValidacao("Ocorreu um erro ao gerar o restore vindo do servidor");
        }
    }

    /**
     * Verifica se existe arquivo de backup gerado
     *
     * @param backup
     */
    public boolean possuiBackup(Backup backup){
        String fileName =  backup.getOrigem().substring(backup.getOrigem().lastIndexOf("/"), backup.getOrigem().length()) + ZipUtil.PREFIXO + ZipUtil.EXT;
        File arquivo = new File(backup.getDestino() + fileName);

        return arquivo.exists();
    }

    /**
     * Verifica se o arquivo nao foi danificado no processo de donwload do servidor para o cliente
     *
     * @param bean
     * @param stream
     */
    public RetornoValidacao isMd5(Backup bean, Stream stream){
        BackupDTO dto = new BackupDTO(bean.getId(), bean.getDescricao(), bean.getOrigem(), bean.getDestino());
        return Hash.compare(stream.getMd5(),dto.getOrigem().substring(0,dto.getOrigem().lastIndexOf("/")),dto);
    }

    /**
     * Atualiza a data de geração do backup
     *
     * @param bean
     */
//    public void atualizaData(Backup bean){
//        bean.setUltimaExecucao(Calendar.getInstance().getTime());
//        save(bean);
//    }

}
